﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;

namespace Storm.TextEditor.Core.Splitting
{
	[ToolboxItem(false)]
	public class SplitViewControl
		: Control
	{
		#region Fields

		private Panel vertical   = null;
		private Panel horizontal = null;
		private Panel center     = null;

		private Control upperLeft  = null;
		private Control upperRight = null;
		private Control lowerLeft  = null;
		private Control lowerRight = null;

		private Point prevPos   = new Point(0);
		private bool  firstTime = false;

		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private Container components = null;

		private SizeAction action   = SizeAction.None;
		private Point      startPos = new Point(0, 0);

		#region Events

		/// <summary>
		/// Occurs when the split view is resizing.
		/// </summary>
		public event EventHandler Resizing = null;

		/// <summary>
		/// Occurs when the top split view is hidden.
		/// </summary>
		public event EventHandler HideTop = null;

		/// <summary>
		/// Occurs when the left split view is hidden.
		/// </summary>
		public event EventHandler HideLeft = null;

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the distance, in pixels, between the vertical split view's left edge and the SplitViewControl's left edge.
		/// </summary>
		public int VerticalEdgeDistance
		{
			get { return vertical.Left; }
			set
			{
				vertical.Left = value;
				this.DoResize();
			}
		}

		/// <summary>
		/// Gets or sets the distance, in pixels, between the horizontal split view's top edge and the SplitViewControl's top edge.
		/// </summary>
		public int HorizontalEdgeDistance
		{
			get { return horizontal.Top; }
			set
			{
				horizontal.Top = value;
				this.DoResize();
			}
		}

		/// <summary>
		/// Gets or sets the Control that should be confined in the upper left split view.
		/// </summary>
		public Control UpperLeft
		{
			get { return upperLeft; }
			set
			{
				upperLeft = value;
				this.DoResize();
			}
		}

		/// <summary>
		/// Gets or sets the Control that should be confined in the upper right split view.
		/// </summary>
		public Control UpperRight
		{
			get { return upperRight; }
			set
			{
				upperRight = value;
				this.DoResize();
			}
		}

		/// <summary>
		/// Gets or sets the Control that should be confined in the lower left split view.
		/// </summary>
		public Control LowerLeft
		{
			get { return lowerLeft; }
			set
			{
				lowerLeft = value;
				this.DoResize();
			}
		}

		/// <summary>
		/// Gets or sets the Control that should be confined in the lower right split view.
		/// </summary>
		public Control LowerRight
		{
			get { return lowerRight; }
			set
			{
				lowerRight = value;
				this.DoResize();
			}
		}

		#endregion

		#region Methods

		#region Protected

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (components != null)
				{
					components.Dispose();
				}
			}

			base.Dispose(disposing);
		}

		protected override void OnResize(EventArgs e)
		{
			// Update by calling DoResize.
			this.DoResize();
		}

		protected override void OnControlAdded(ControlEventArgs e)
		{
			// Update by calling DoResize.
			this.DoResize();
		}

		#endregion

		#region Private

		/// <summary>
		/// Raises the Resizing event.
		/// </summary>
		private void OnResizing()
		{
			if (this.Resizing != null)
				this.Resizing(this, new EventArgs());
		}

		/// <summary>
		/// Raises the HideLeft event.
		/// </summary>
		private void OnHideLeft()
		{
			if (this.HideLeft != null)
				this.HideLeft(this, new EventArgs());
		}

		/// <summary>
		/// Raises the HideTop event.
		/// </summary>
		private void OnHideTop()
		{
			if (this.HideTop != null)
				this.HideTop(this, new EventArgs());
		}

		/// <summary>
		/// Contains logic for updating the visible Controls in the SplitViewControl.
		/// </summary>
		private void DoResize()
		{
			int newHeight = this.Height;
			int newWidth = this.Width;

			if (newHeight != 0 && newWidth != 0)
			{
				this.SuspendLayout();

				this.Resize2();
				this.OnResizing();

				if (horizontal.Top < 15)
				{
					horizontal.Top = 0 - horizontal.Height;
					this.OnHideTop();
				}

				if (vertical.Left < 15)
				{
					vertical.Left = 0 - vertical.Width;
					this.OnHideLeft();
				}

				horizontal.Width = this.Width;
				vertical.Height  = this.Height;

				horizontal.SendToBack();
				vertical.SendToBack();

				horizontal.BackColor = SystemColors.Control;
				vertical.BackColor   = SystemColors.Control;

				int rightLeft     = vertical.Left + vertical.Width;
				int rightLowerTop = horizontal.Top + horizontal.Height;
				int rightWidth    = this.Width - rightLeft;
				int lowerHeight   = this.Height - rightLowerTop;
				int upperHeight   = horizontal.Top;
				int leftWidth     = vertical.Left;

				if (lowerRight != null)
				{
					lowerRight.BringToFront();
					lowerRight.SetBounds(rightLeft, rightLowerTop, rightWidth, lowerHeight);
				}
				if (upperRight != null)
				{
					upperRight.BringToFront();
					upperRight.SetBounds(rightLeft, 0, rightWidth, upperHeight);
				}

				if (lowerLeft != null)
				{
					lowerLeft.BringToFront();
					lowerLeft.SetBounds(0, rightLowerTop, leftWidth, lowerHeight);
				}

				if (upperLeft != null)
				{
					upperLeft.BringToFront();
					upperLeft.SetBounds(0, 0, leftWidth, upperHeight);
				}

				this.ResumeLayout();
			}
		}

		private void OnHorizontalMouseDown(object sender, MouseEventArgs e)
		{
			action = SizeAction.SizeH;
			startPos = new Point(e.X, e.Y);

			horizontal.BringToFront();
			horizontal.BackColor = SystemColors.ControlDark;

			firstTime = true;
		}

		private void OnVerticalMouseDown(object sender, MouseEventArgs e)
		{
			action = SizeAction.SizeV;
			startPos = new Point(e.X, e.Y);

			vertical.BringToFront();
			vertical.BackColor = SystemColors.ControlDark;

			firstTime = true;
		}

		private void OnCenterMouseDown(object sender, MouseEventArgs e)
		{
			action = SizeAction.SizeA;
			startPos = new Point(e.X, e.Y);

			vertical.BringToFront();
			horizontal.BringToFront();

			vertical.BackColor = SystemColors.ControlDark;
			horizontal.BackColor = SystemColors.ControlDark;

			firstTime = true;
		}

		private void OnHorizontalMouseUp(object sender, MouseEventArgs e)
		{
			int x = 0;
			int y = startPos.Y - e.Y;
			this.Resize(x, y);

			action = SizeAction.None;
			this.DoResize();	// Update
		}

		private void OnVerticalMouseUp(object sender, MouseEventArgs e)
		{
			int x = startPos.X - e.X;
			int y = 0;
			this.Resize(x, y);

			action = SizeAction.None;
			this.DoResize();	// Update.
		}

		private void OnCenterMouseUp(object sender, MouseEventArgs e)
		{
			int x = startPos.X - e.X;
			int y = startPos.Y - e.Y;
			this.Resize(x, y);

			action = SizeAction.None;
			this.DoResize();	// Update.
		}

		private void OnHorizontalMouseMove(object sender, MouseEventArgs e)
		{
			if (action == SizeAction.SizeH && e.Button == MouseButtons.Left)
			{
				Point start;
				int x = e.X;
				int y = e.Y;

				if (y + horizontal.Top > Height - 4)
					y = Height - 4 - horizontal.Top;
				if (y + horizontal.Top < 0)
					y = 0 - horizontal.Top;

				if (firstTime == false)
				{
					start = this.PointToScreen(this.Location);
					start.Y += prevPos.Y + horizontal.Location.Y;
					ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, this.Width, 3), Color.Black);
				}
				else
					firstTime = false;

				start = this.PointToScreen(this.Location);
				start.Y += y + horizontal.Location.Y;
				ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, this.Width, 3), Color.Black);

				prevPos = new Point(x, y);
			}
		}

		private void OnVerticalMouseMove(object sender, MouseEventArgs e)
		{
			if (action == SizeAction.SizeV && e.Button == MouseButtons.Left)
			{
				Point start;
				int x = e.X;
				int y = e.Y;

				if (x + vertical.Left > Width - 4)
					x = Width - 4 - vertical.Left;
				if (x + vertical.Left < 0)
					x = 0 - vertical.Left;

				if (firstTime == false)
				{
					start = this.PointToScreen(this.Location);
					start.X += prevPos.X + vertical.Location.X;
					ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, 3, this.Height), Color.Black);
				}
				else
					firstTime = false;

				start = this.PointToScreen(this.Location);
				start.X += x + vertical.Location.X;
				ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, 3, this.Height), Color.Black);

				prevPos = new Point(x, y);
			}
		}

		private void OnCenterMouseMove(object sender, MouseEventArgs e)
		{
			if (action == SizeAction.SizeA && e.Button == MouseButtons.Left)
			{
				Point start;
				int x = e.X - center.Width / 2;
				int y = e.Y - center.Height / 2;

				if (firstTime == false)
				{
					start = this.PointToScreen(this.Location);
					start.X += prevPos.X + vertical.Location.X;
					ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, 3, this.Height), Color.Black);

					start = this.PointToScreen(this.Location);
					start.Y += prevPos.Y + horizontal.Location.Y;
					ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, this.Width, 3), SystemColors.ControlDark);
				}
				else
					firstTime = false;

				start = this.PointToScreen(this.Location);
				start.X += x + vertical.Location.X;
				ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, 3, this.Height), Color.Black);

				start = this.PointToScreen(this.Location);
				start.Y += y + horizontal.Location.Y;
				ControlPaint.FillReversibleRectangle(new Rectangle(start.X, start.Y, this.Width, 3), SystemColors.ControlDark);

				prevPos = new Point(x, y);
			}
		}

		private new void Resize(int x, int y)
		{
			this.SuspendLayout();

			int xx = vertical.Left - x;
			int yy = horizontal.Top - y;

			if (xx < 0)
				xx = 0;

			if (yy < 0)
				yy = 0;

			if (yy > this.Height - horizontal.Height - SystemInformation.VerticalScrollBarWidth * 3)
				yy = this.Height - horizontal.Height - SystemInformation.VerticalScrollBarWidth * 3;


			if (xx > this.Width - vertical.Width - SystemInformation.VerticalScrollBarWidth * 3)
				xx = this.Width - vertical.Width - SystemInformation.VerticalScrollBarWidth * 3;

			if (xx != vertical.Left)
				vertical.Left = xx;

			if (yy != horizontal.Top)
				horizontal.Top = yy;

			int centerY = (horizontal.Top + horizontal.Height / 2) - center.Height / 2;
			int centerX = (vertical.Left + vertical.Width / 2) - center.Width / 2;

			center.Location = new Point(centerX, centerY);
			this.ResumeLayout();
			this.Invalidate();

			try
			{
				if (upperLeft != null)
					upperLeft.Refresh();

				if (upperLeft != null)
					upperLeft.Refresh();

				if (upperLeft != null)
					upperLeft.Refresh();

				if (upperLeft != null)
					upperLeft.Refresh();
			}
			catch
			{
			}

			this.OnResizing();
		}

		private void Resize2()
		{
			int xx = vertical.Left;
			int yy = horizontal.Top;

			if (xx < 0)
				xx = 0;

			if (yy < 0)
				yy = 0;

			if (yy > this.Height - horizontal.Height - SystemInformation.VerticalScrollBarWidth * 3)
			{
				yy = this.Height - horizontal.Height - SystemInformation.VerticalScrollBarWidth * 3;
				if (yy != horizontal.Top)
					horizontal.Top = yy;
			}

			if (xx > this.Width - vertical.Width - SystemInformation.VerticalScrollBarWidth * 3)
			{
				xx = this.Width - vertical.Width - SystemInformation.VerticalScrollBarWidth * 3;
				if (xx != vertical.Left)
					vertical.Left = xx;
			}

			int centerY = (horizontal.Top + horizontal.Height / 2) - center.Height / 2;
			int centerX = (vertical.Left + vertical.Width / 2) - center.Width / 2;

			center.Location = new Point(centerX, centerY);
		}

		private void OnCenterDoubleClick(object sender, EventArgs e)
		{
			horizontal.Top = -100;
			vertical.Left = -100;
			this.DoResize();
		}

		private void OnVerticalDoubleClick(object sender, EventArgs e)
		{
			vertical.Left = -100;
			this.DoResize();
		}

		private void OnHorizontalDoubleClick(object sender, EventArgs e)
		{
			horizontal.Top = -100;
			this.DoResize();
		}

		#endregion

		#region Public

		/// <summary>
		/// Splits the horizontal split view 50 percent.
		/// </summary>
		public void SplitHorizontalHalf()
		{
			horizontal.Top = Height / 2;
			this.DoResize();
		}

		/// <summary>
		/// Splits the vertical split view 50 percent.
		/// </summary>
		public void SplitVerticalHalf()
		{
			vertical.Left = Width / 2;
			this.DoResize();
		}

		/// <summary>
		/// Resets the split views.
		/// </summary>
		public void ResetSplitview()
		{
			vertical.Left = 0;
			horizontal.Top = 0;
			this.DoResize();
		}

		/// <summary>
		/// Start dragging the horizontal splitter.
		/// </summary>
		public void InvokeMouseDownHorizontal()
		{
			IntPtr hwnd = horizontal.Handle;
			NativeUser32Api.SendMessage(hwnd, (int)WindowMessage.WM_LBUTTONDOWN, 0, 0);
		}

		/// <summary>
		/// Start dragging the vertical splitter.
		/// </summary>
		public void InvokeMouseDownVertical()
		{
			IntPtr hwnd = vertical.Handle;
			NativeUser32Api.SendMessage(hwnd, (int)WindowMessage.WM_LBUTTONDOWN, 0, 0);
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of SplitViewControl.
		/// </summary>
		public SplitViewControl()
		{
			// Required by the Windows Forms Designer.
			InitializeComponent();

			this.SetStyle(ControlStyles.ContainerControl, true);

			this.DoResize();
			this.Resize(0, 0);
		}

		#region Component Designer generated code

		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			vertical = new System.Windows.Forms.Panel();
			horizontal = new System.Windows.Forms.Panel();
			center = new System.Windows.Forms.Panel();
			SuspendLayout();
			// 
			// Vertical
			// 
			vertical.BackColor = System.Drawing.SystemColors.Control;
			vertical.Cursor = System.Windows.Forms.Cursors.VSplit;
			vertical.Name = "Vertical";
			vertical.Size = new System.Drawing.Size(4, 264);
			vertical.TabIndex = 0;
			vertical.MouseUp += new System.Windows.Forms.MouseEventHandler(OnVerticalMouseUp);
			vertical.DoubleClick += new System.EventHandler(OnVerticalDoubleClick);
			vertical.MouseMove += new System.Windows.Forms.MouseEventHandler(OnVerticalMouseMove);
			vertical.MouseDown += new System.Windows.Forms.MouseEventHandler(OnVerticalMouseDown);
			// 
			// Horizontal
			// 
			horizontal.BackColor = System.Drawing.SystemColors.Control;
			horizontal.Cursor = System.Windows.Forms.Cursors.HSplit;
			horizontal.Name = "Horizontal";
			horizontal.Size = new System.Drawing.Size(320, 4);
			horizontal.TabIndex = 1;
			horizontal.MouseUp += new System.Windows.Forms.MouseEventHandler(OnHorizontalMouseUp);
			horizontal.DoubleClick += new System.EventHandler(OnHorizontalDoubleClick);
			horizontal.MouseMove += new System.Windows.Forms.MouseEventHandler(OnHorizontalMouseMove);
			horizontal.MouseDown += new System.Windows.Forms.MouseEventHandler(OnHorizontalMouseDown);
			// 
			// Center
			// 
			center.BackColor = System.Drawing.SystemColors.Control;
			center.Cursor = System.Windows.Forms.Cursors.SizeAll;
			center.Location = new System.Drawing.Point(146, 69);
			center.Name = "Center";
			center.Size = new System.Drawing.Size(24, 24);
			center.TabIndex = 2;
			center.MouseUp += new System.Windows.Forms.MouseEventHandler(OnCenterMouseUp);
			center.DoubleClick += new System.EventHandler(OnCenterDoubleClick);
			center.MouseMove += new System.Windows.Forms.MouseEventHandler(OnCenterMouseMove);
			center.MouseDown += new System.Windows.Forms.MouseEventHandler(OnCenterMouseDown);
			// 
			// SplitViewControl
			// 
			BackColor = System.Drawing.Color.Magenta;
			Controls.AddRange(new System.Windows.Forms.Control[]
				{
					center,
					horizontal,
					vertical
				});
			Size = new System.Drawing.Size(200, 200);
			ResumeLayout(false);
		}

		#endregion
	}
}
